package main

import (
	"log"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func init() {
	// 连接数据库
	// DSN (Data Source Name) 格式：username:password@(host:port)/database?charset=utf8mb4&parseTime=True&loc=Local
	// parseTime=True 是为了让 GORM 能正确解析 MySQL 中的 DATETIME 和 TIMESTAMP 类型
	// loc=Local 是为了让时间以本地时区解析
	dsn := "root:vader20011014@(localhost:3306)/study?charset=utf8mb4&parseTime=True&loc=Local"
	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("failed to connect database: %v", err)
	}

	// 自动迁移
	// GORM 会根据结构体定义自动创建或更新表结构
	err = DB.AutoMigrate(&Person{})
	if err != nil {
		log.Fatalf("failed to auto migrate: %v", err)
	}
	log.Println("Database connection and migration successful!")
}

// 定义数据结构
type Person struct {
	// GORM 约定 `ID` 或 `Id` 字段为主键，会自动识别为自增主键。
	// 这里我们使用 `UserId` 来映射 `id` 列，并且指定其为主键。
	UserId   string `gorm:"column:id;primaryKey"` // 对应数据库的 id 列，并且是主键
	Username string `gorm:"column:name"`          // 对应数据库的 name 列
	Age      int    `gorm:"column:age"`           // 对应数据库的 age 列
	Address  string `gorm:"column:address"`       // 对应数据库的 address 列
}

// TableName 方法用于指定 GORM 对应的数据库表名
func (Person) TableName() string {
	return "user" // 将 Person 结构体映射到 user 表
}

// -----查询-----
func query() {
	var p Person
	// First 根据主键查找第一条记录
	// DB.First(&p, "12132")

	// Where 子句用于构建查询条件
	result := DB.Where("id = ?", "12132").First(&p)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			log.Printf("query fail: record not found for id %s\n", "12132")
		} else {
			log.Fatalf("query fail: %v\n", result.Error)
		}
		return
	}
	log.Printf("query succ: %v\n", p)
}

func list() {
	var ps []Person
	// Find 查询所有记录
	result := DB.Find(&ps)
	if result.Error != nil {
		log.Fatalf("list fail: %v\n", result.Error)
		return
	}
	for _, p := range ps {
		log.Printf("list succ: %v\n", p)
	}
}

// -----新增-----
func insert() {
	newPerson := Person{UserId: "1145", Username: "alan", Age: 24, Address: "China"}
	result := DB.Create(&newPerson)
	if result.Error != nil {
		log.Fatalf("insert fail: %v\n", result.Error)
		return
	}
	// 如果主键是自增的，GORM 会自动填充到结构体中
	log.Printf("insert succ, ID: %s, RowsAffected: %d\n", newPerson.UserId, result.RowsAffected)
}

// -----更新-----
func update() {
	// 更新多个字段
	result := DB.Model(&Person{}).Where("id = ?", "1145").Updates(map[string]interface{}{"name": "alan223", "age": 25})

	if result.Error != nil {
		log.Fatalf("update fail: %v\n", result.Error)
		return
	}
	if result.RowsAffected == 0 {
		log.Printf("update warning: no records updated for id %s\n", "1145")
	} else {
		log.Println("update succ")
	}
}

// -----删除-----
func delete() {
	// 硬删除 (物理删除)
	result := DB.Where("id = ?", "1145").Delete(&Person{})

	if result.Error != nil {
		log.Fatalf("delete fail: %v\n", result.Error)
		return
	}
	if result.RowsAffected == 0 {
		log.Printf("delete warning: no records deleted for id %s\n", "1145")
	} else {
		log.Println("delete succ")
	}
}

// -----事务-----
// GORM 推荐使用块事务
func tx() {
	err := DB.Transaction(func(tx *gorm.DB) error {
		// 事务中的所有操作都使用 tx 对象

		// 插入一条记录
		newPerson := Person{UserId: "9999", Username: "tx_test", Age: 30, Address: "Transaction Land"}
		if err := tx.Create(&newPerson).Error; err != nil {
			log.Printf("transaction insert failed: %v\n", err)
			return err // 返回错误，事务将回滚
		}

		// 更新一条记录
		// 注意：更新不存在的ID并不会报错，只会影响0行。如果业务需要严格控制，需要单独检查RowsAffected
		if err := tx.Model(&Person{}).Where("id = ?", "12132").Update("name", "UpdatedByTx").Error; err != nil {
			log.Printf("transaction update failed: %v\n", err)
			return err // 返回错误，事务将回滚
		}

		// 如果所有操作都成功，GORM 会自动提交事务
		return nil
	})

	if err != nil {
		log.Printf("transaction failed: %v\n", err)
	} else {
		log.Println("transaction succ")
	}
}

func main() {
	query()
	insert() // 先插入一条记录，方便后续操作
	update()
	delete()
	list()
	tx() // 执行事务
}
